package it.enricod.jcontextfree.engine;

import it.enricod.jcontextfree.engine.model.DataModelFactory;
import it.enricod.jcontextfree.engine.model.IProgram;
import it.enricod.jcontextfree.engine.model.IRuleDef;
import it.enricod.jcontextfree.engine.model.IShape;
import it.enricod.jcontextfree.engine.model.JContextFreeConstants;
import it.enricod.jcontextfree.engine.sablecc.analysis.DepthFirstAdapter;
import it.enricod.jcontextfree.engine.sablecc.node.ABFloatingPointValueSingleValue;
import it.enricod.jcontextfree.engine.sablecc.node.ABIntegerValueSingleValue;
import it.enricod.jcontextfree.engine.sablecc.node.AFFloatingPointValueSingleValue;
import it.enricod.jcontextfree.engine.sablecc.node.AFIntegerValueSingleValue;
import it.enricod.jcontextfree.engine.sablecc.node.AProgram;
import it.enricod.jcontextfree.engine.sablecc.node.AProgramHeading;
import it.enricod.jcontextfree.engine.sablecc.node.AProgramStartshape;
import it.enricod.jcontextfree.engine.sablecc.node.ARFloatingPointValueSingleValue;
import it.enricod.jcontextfree.engine.sablecc.node.ARIntegerValueSingleValue;
import it.enricod.jcontextfree.engine.sablecc.node.ARuleDecl;
import it.enricod.jcontextfree.engine.sablecc.node.ARuleDef;
import it.enricod.jcontextfree.engine.sablecc.node.ASFloatingPointValueSingleValue;
import it.enricod.jcontextfree.engine.sablecc.node.ASIntegerValueSingleValue;
import it.enricod.jcontextfree.engine.sablecc.node.AShapeDecl;
import it.enricod.jcontextfree.engine.sablecc.node.AXFloatingPointValueSingleValue;
import it.enricod.jcontextfree.engine.sablecc.node.AXIntegerValueSingleValue;
import it.enricod.jcontextfree.engine.sablecc.node.AYFloatingPointValueSingleValue;
import it.enricod.jcontextfree.engine.sablecc.node.AYIntegerValueSingleValue;

import java.util.Stack;

import org.apache.log4j.Logger;

/**
 * classe principale che fa il parsing del programma e costruisce gli oggetti
 * di modello
 * 
 * @author edonelli
 */
public class JCFProgramInterpreter extends DepthFirstAdapter {

	private Logger logger = null;
	
	private IProgram program;
	private DataModelFactory dataModelFactory = new DataModelFactory();
	private Stack<IRuleDef> rulesStack = new Stack<IRuleDef>();
	
	private IShape currentShape = null;
	
	public JCFProgramInterpreter() {
		logger = Logger.getLogger("JCFProgramInterpreter");
		
	}
	
//	public void caseAProgram(AProgram node) {
//		System.out.println( node );
//		String lhs = node.getLeft().getText().trim();
//		String rhs = node.getRight().getText().trim();
//		int result = (new Integer(lhs)).intValue() + (new Integer(rhs)).intValue();
//		System.out.println(lhs + " + " + rhs + " = " + result );
//	}
	
	/**
	 * 
	 */
	public void inAProgram(AProgram node) {
//		System.out.println("in AProgram " + node );
	}
	
	/**
	 * 
	 */
	public void inAProgramStartshape(AProgramStartshape node) {
//		Log.debug( "in AProgramHeading | " + node );
		program = dataModelFactory.createProgram( node.getIdentifier().getText()  );
	}

	public void outAProgramHeading(AProgramHeading node) {
		logger.debug( "out AProgramHeading | " + node );
	}
	
	public void outAProgram(AProgram node ) {
		logger.debug("out AProgram");
	}
	
	public void inARule( ARuleDef node) {
//		System.out.println("in rule '" + node + "'");
	}
	
	public void outARule( ARuleDef node) {
		IRuleDef ruleDef = rulesStack.pop();
		logger.debug("outARule | fine esame regola: " + ruleDef.getRuleName() );
	}
	
	public void inARuleDecl(ARuleDecl node) {
		String ruleName = node.getIdentifier().getText();
		logger.debug("esamino regola: " + ruleName );
		IRuleDef aRuleDef = dataModelFactory.createRuleDef(ruleName, 1.0f);
		program.addRuleDef( ruleName.trim(), aRuleDef);
		rulesStack.add(aRuleDef);
	}
	
	public void outARuleDecl(ARuleDecl node) {
		String ruleName = node.getIdentifier().getText();
	}
	
	public void inAShapeDecl( AShapeDecl node) {
		String shapeName = node.getIdentifier().getText();
		IRuleDef aRuleDef = rulesStack.lastElement();
		currentShape = dataModelFactory.createShape(shapeName);
		aRuleDef.addShape( currentShape );
		logger.debug("  shape: " + shapeName );

	}
	
	public void outAShapeDecl( AShapeDecl node) {
		currentShape = null;
	}	

	public void inAXFloatingPointValueSingleValue(AXFloatingPointValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getFloatingPointLiteral().getText());
		if (neg) result *= -1;
		currentShape.writeAttribute("x", result);
	}

	public void inARFloatingPointValueSingleValue(ARFloatingPointValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getFloatingPointLiteral().getText());
		if (neg) result *= -1;
		currentShape.writeAttribute("r", Math.toRadians(result));
	}

	
	public void inAXIntegerValueSingleValue(AXIntegerValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getIntegerLiteral().getText());
		if (neg) result *= -1;

		currentShape.writeAttribute("x", result);
	}	
	
	public void inAYFloatingPointValueSingleValue(AYFloatingPointValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getFloatingPointLiteral().getText());
		if (neg) result *= -1;		
		currentShape.writeAttribute("y", result);
	}

	
	
	public void inAYIntegerValueSingleValue(AYIntegerValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getIntegerLiteral().getText());
		if (neg) result *= -1;
		currentShape.writeAttribute("y", result);
	}

	public void inARIntegerValueSingleValue(ARIntegerValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getIntegerLiteral().getText());
		if (neg) result *= -1;
		currentShape.writeAttribute("r", Math.toRadians(result)  );
	}
	
	public void inASIntegerValueSingleValue(ASIntegerValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getIntegerLiteral().getText());
		if (neg) result *= -1;
		currentShape.writeAttribute("s", result   );
	}

	public void inASFloatingPointValueSingleValue(ASFloatingPointValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getFloatingPointLiteral().getText());
		if (neg) result *= -1;
		currentShape.writeAttribute("s", result );
	}

	public void inABIntegerValueSingleValue(ABIntegerValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getIntegerLiteral().getText());
		if (neg) result *= -1;
		currentShape.writeAttribute("b", result);
	}

	public void inABFloatingPointValueSingleValue(ABFloatingPointValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getFloatingPointLiteral().getText());
		if (neg) result *= -1;
		currentShape.writeAttribute("b", result);
	}
	
	public void inAFIntegerValueSingleValue(AFIntegerValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getIntegerLiteral().getText());
		if (neg) result *= -1;
		currentShape.writeAttribute("f", result);
	}

	public void inAFFloatingPointValueSingleValue(AFFloatingPointValueSingleValue valueNode) {
		boolean neg = false;
		if (valueNode.getMinus() != null ) {
			neg = true;
		}
		double result = new Double(valueNode.getFloatingPointLiteral().getText());
		if (neg) result *= -1;
		currentShape.writeAttribute("f", result);
	}
	public IProgram getProgram() {
		return program;
	}
	
}
